#include "RHISemaphore.h"
#include "../../Game/Process/RenderProcess.h"

RHISemaphore::BuilderDetails::BuilderDetails()
{
	semaphoreType = VK_SEMAPHORE_TYPE_BINARY;
	count = GRenderState->GetFrameCount();
	waitDstStage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
	isSignaled = false;
}

RHISemaphore::Builder::Builder() noexcept = default;
RHISemaphore::Builder::~Builder() noexcept = default;
RHISemaphore::Builder::Builder(RHISemaphore::Builder const& rhs) noexcept = default;
RHISemaphore::Builder::Builder(RHISemaphore::Builder && rhs) noexcept = default;
RHISemaphore::Builder& RHISemaphore::Builder::operator=(RHISemaphore::Builder const& rhs) noexcept = default;
RHISemaphore::Builder& RHISemaphore::Builder::operator=(RHISemaphore::Builder && rhs) noexcept = default;

RHISemaphore::Builder& RHISemaphore::Builder::SetSemaphoreType(VkSemaphoreType semaphoreType) noexcept
{
	mImpl->semaphoreType = semaphoreType;
	return *this;
}

RHISemaphore::Builder& RHISemaphore::Builder::SetCount(uint32 count) noexcept
{
	mImpl->count = count;
	return *this;
}

RHISemaphore::Builder& RHISemaphore::Builder::SetWaitDstStage(VkPipelineStageFlags waitDstStage) noexcept
{
	mImpl->waitDstStage = waitDstStage;
	return *this;
}

RHISemaphore::Builder& RHISemaphore::Builder::SetSignaled(bool isSignaled) noexcept
{
	mImpl->isSignaled = isSignaled;
	return *this;
}

RefCountPtr<RHISemaphore> RHISemaphore::Builder::Build()
{
	assert(IsInRenderThread());
	return RefCountPtr<RHISemaphore>(NewObject<RHISemaphore>(*mImpl));
}

RefCountPtr<RHISemaphore> RHISemaphore::Builder::Build(RenderProcess * process, const std::string & name)
{
	assert(IsInRenderThread());
	RHISemaphore* semaphore = NewObject<RHISemaphore>(*mImpl);
	process->AddSemaphore(name, semaphore);
	return RefCountPtr<RHISemaphore>(semaphore);
}

void RHISemaphore::ReleaseResource()
{
	if (IsInRenderThread())
	{
		if (mProcess != nullptr)
		{
			mProcess->RemoveSemaphore(mName, this);
		}
		DeleteObject<RHISemaphore>(this);
	}
	else
	{
		AsyncTask(GRenderThreadId, [this]() {
			if (mProcess != nullptr)
			{
				mProcess->RemoveSemaphore(mName, this);
			}
			DeleteObject<RHISemaphore>(this);
			});
	}
}

RHISemaphore::~RHISemaphore()
{
	for (uint32 i = 0; i < mSemaphores.size(); ++i)
	{
		GRenderState->DelayGCSemaphore(mSemaphores[i]);
	}
}

VkSemaphore RHISemaphore::GetSemaphore(uint32 index) const
{
	if (index == UINT32_MAX)
	{
		assert(mSemaphores.size() == GRenderState->GetFrameCount() || mSemaphores.size() == 1);
		return mSemaphores.size() == 1 ? mSemaphores[0] : mSemaphores[GRenderState->GetFrameIndex()];
	}
	assert(index < mSemaphores.size());
	return mSemaphores[index];
}

VkPipelineStageFlags RHISemaphore::GetWaitDstStage() const
{
	return mWaitDstStage;
}

RHISemaphore::RHISemaphore(BuilderDetails & builderDetails)
{
	VkSemaphoreCreateInfo semaphoreCreateInfo{};
	semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;

	mSemaphores.resize(builderDetails.count, VK_NULL_HANDLE);
	for (uint32 i = 0; i < builderDetails.count; ++i)
	{
		VK_CHECK_RESULT(vkCreateSemaphore(GVulkanRHI->GetLogicalDevice(), &semaphoreCreateInfo, nullptr, &mSemaphores[i]));
	}

	if (builderDetails.isSignaled)
	{
		VkSubmitInfo submitInfo{};
		submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;

		submitInfo.signalSemaphoreCount = builderDetails.count;
		submitInfo.pSignalSemaphores = mSemaphores.data();
		VK_CHECK_RESULT(vkQueueSubmit(GVulkanRHI->GetQueue(), 1, &submitInfo, VK_NULL_HANDLE));
		VK_CHECK_RESULT(vkQueueWaitIdle(GVulkanRHI->GetQueue()));
	}

	mWaitDstStage = builderDetails.waitDstStage;
}

